package com.telenav.snowsweeper.renderer;

import java.util.AbstractList;
import java.util.Vector;

import com.telenav.snowsweeper.controller.MapEdgeTouchEventHandler;
import com.telenav.snowsweeper.data.DisplayUtil;
import com.telenav.snowsweeper.framework.ui.IObjectRenderer;
import com.telenav.snowsweeper.framework.ui.IObjectRenderer.Layer;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.SystemClock;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class SurfaceRenderer implements SurfaceHolder.Callback {
	Thread renderer;
	SurfaceView delegate;
	SurfaceHolder holder;
	
	boolean isRunning = true;
	boolean isPaused = false;
	Object run_mutex = new Object();
	
	Vector<IObjectRenderer> elements = new Vector<IObjectRenderer>();
	Vector<IObjectRenderer> elementsBuffer = new Vector<IObjectRenderer>();
	Object data_mutex = new Object();
	int spanTime = 60;		// 60ms pre frame
	
	Layer[] priority;
	boolean[] layersFlag;
	
	
	public SurfaceRenderer(SurfaceView view) {
		this.delegate = view;
		holder = delegate.getHolder();
		holder.addCallback(this);
		init();
		renderer = new Renderer();
	}
	
	private void init() {
		priority = new Layer[] {
				Layer.INFO_UNDER_Edge,
				Layer.BACKGROUND,
				Layer.Edge,
				Layer.INFO_ON_Edge,
				Layer.Route,
				Layer.CAR,
				Layer.TOP,	
		};
		layersFlag = new boolean[priority.length];
		for (int i = 0; i < layersFlag.length; ++i) {
			layersFlag[i] = true;
		}
	}
	
	public void pause() {
		synchronized (run_mutex) {
			isPaused = true;
		}
	}
	
	public void resume() {
		synchronized (run_mutex) {
			isPaused = false;
			run_mutex.notify();
		}
	}
	
	public void swapDrawBuffer() {
		syncMoveElements(elementsBuffer, elements);
	}
	
	public void addObjectRenderer(IObjectRenderer renderer) {
		synchronized (data_mutex) {
			elements.add(renderer);
		}
	}
	
	private void syncMoveElements(AbstractList<IObjectRenderer> src, AbstractList<IObjectRenderer> dst) {
		synchronized (data_mutex) {
			dst.clear();
			dst.addAll(src);
			src.clear();
		}
	}
	
	private void syncCopyElements(AbstractList<IObjectRenderer> src, AbstractList<IObjectRenderer> dst) {
		synchronized (data_mutex) {
			dst.addAll(src);
		}
	}
	
	private void updateAndRender() {
		Canvas canvas = holder.lockCanvas();
		canvas.drawARGB(Color.alpha(DisplayUtil.COLOR_BACKGROUND), 
						Color.red(DisplayUtil.COLOR_BACKGROUND), 
						Color.green(DisplayUtil.COLOR_BACKGROUND), 
						Color.blue(DisplayUtil.COLOR_BACKGROUND));
		try {
			Vector<IObjectRenderer> oneFrame = new Vector<IObjectRenderer>();
			syncCopyElements(elements, oneFrame);
			if (oneFrame.size() > 0) {
				for (int i = 0; i < layersFlag.length; ++i) {
					if (layersFlag[i]) {
						for (IObjectRenderer o : oneFrame) {
							o.render(canvas, priority[i]);
						}
					}
				}
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			holder.unlockCanvasAndPost(canvas);
		}
	}
	
	class Renderer extends Thread {
		@Override
		public void run() {
			long time = 0;
			while (isRunning) {
				if (isPaused) {
					synchronized (run_mutex) {
						while (isPaused) {
							try {
								run_mutex.wait(2000);
							}
							catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
				
				time = SystemClock.uptimeMillis();
				updateAndRender();
				time = 60 - (SystemClock.uptimeMillis() - time) - 5;
				if (time > 0) {
					try {
						sleep(time);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		this.holder = holder;
		Util.canvasWidth = delegate.getWidth();
		Util.canvasHeight = delegate.getHeight();
		renderer.start();
		delegate.requestFocus();
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		this.holder = holder;
		Util.canvasWidth = width;
		Util.canvasHeight = height;
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		isRunning = false;
		resume();
		try {
			renderer.join();
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
